﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Reflection;
using WyUnity.Data.DataAccess;
using WyUnity.Utility;
using WyUnity.Utility.Dynamic;
using WyUnity.Utility.ExtentionMethods;

namespace WyUnity.Data.TableMapper
{
    /// <summary>
    /// 只读数据映射对象的基础实现
    /// </summary>
    public abstract class ViewBase<T> : IView where T : ViewBase<T>
    {
        string IMapObject.GetSQLClip(DataAccess.ISqlDialect dialect)
        {
            return string.Format("({0}) as TEMPTABLE", GetSQLClip());
        }

        public abstract string GetSQLClip();

        void IMapObject.LoadData(IDataReader dataReader)
        {
            foreach (var ps in _propsSetMethodCache)
            {
                var value = dataReader[ps.Key];
                if (value.IsNullOrDBNull()) continue;
                value = value.TryTo(ps.Value.PropertyInfo.PropertyType);
                ps.Value.Function(this, new object[] { value });
            }
        }
        
        #region 静态初始化
        /// <summary>
        /// 对象全部属性设置方法
        /// </summary>
        protected static readonly Dictionary<string, PropertyMapping> _propsSetMethodCache;
        /// <summary>
        /// 对象全部属性读取方法
        /// </summary>
        protected static readonly Dictionary<string, PropertyMapping> _propsGetMethodCache;
        /// <summary>
        /// 主键列表
        /// </summary>
        protected internal static readonly List<string> _keys;

        protected internal static readonly List<FieldDefine> _columns;
        static ViewBase()
        {
            _propsGetMethodCache = new Dictionary<string, PropertyMapping>();
            _propsSetMethodCache = new Dictionary<string, PropertyMapping>();
            _keys = new List<string>();
            _columns = new List<FieldDefine>();

            var type = typeof(T);
            var props = type.GetProperties();
            foreach (var p in props)
            {
                var atts = p.GetCustomAttributes(typeof(MapAttribute), true);
                if (atts.Length == 1)
                {
                    var mapattr = atts[0] as MapAttribute;

                    //以Map的FieldName为Key
                    var pname = mapattr.Name;
                    //如果没有设置FieldName，则已字段的名称为Key
                    if (pname.IsNullOrEmpty()) pname = p.Name;

                    if (mapattr.IsKeyField) _keys.Add(pname);

                    System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(pname));
                    //Log.Default.Debug(pname);

                    _columns.Add(new FieldDefine(pname, p.PropertyType, mapattr.IsKeyField) { Length = mapattr.Length, IsRowVersion = (mapattr is RowVersionAttribute) });

                    if (p.CanRead)
                    {
                        if (_propsGetMethodCache.ContainsKey(pname))
                            throw new DAException("字段名称重复");

                        _propsGetMethodCache.Add(pname,
                            new PropertyMapping
                            {
                                Function = DynamicCall.GetExecuteDelegate(p.GetGetMethod()),
                                MapConfig = mapattr,
                                PropertyInfo = p,
                            });
                    }
                    if (p.CanWrite)
                    {
                        if (_propsSetMethodCache.ContainsKey(pname))
                            throw new DAException("字段名称重复");

                        _propsSetMethodCache.Add(pname,
                            new PropertyMapping
                            {
                                Function = DynamicCall.GetExecuteDelegate(p.GetSetMethod()),
                                MapConfig = mapattr,
                                PropertyInfo = p,
                            });
                    }
                }
            }
        }
        #endregion

        public struct PropertyMapping
        {
            public Func<object, object[], object> Function { get; set; }
            public PropertyInfo PropertyInfo { get; set; }
            public MapAttribute MapConfig { get; set; }
        }


    }
}